#include "mainControl.h"
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_sleep.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "sdkconfig.h"
#include "esp_peripherals.h"
#include "board.h"
#include "periph_button.h"

#include "btPhoneMode.h"
// #include "headPhoneMode.h"
#include "audio_event_iface.h"

#include "driver/rtc_io.h"
#include "esp32/ulp.h"
#include "soc/rtc.h"		  // for wakeup trigger defines
#include "soc/rtc_cntl_reg.h" // for read rtc registers directly (cause)
#include "soc/soc.h"		  // for direct register read macros
#include "rom/rtc.h"
#include <time.h>
#include <sys/time.h>

#include <u8g2.h>
#include "u8g2_esp32_hal.h"
#include "image.h"
#include "driver/gpio.h"
#include "driver/adc.h"
#include "esp_adc_cal.h"
#include "equalizer.h"

#define SUPPORT_OLED
#define BAT_MEASURE_PERIOD 5000000   //us
#define DEFAULT_VREF 3300	//Use adc2_vref_to_gpio() to obtain a better estimate
#define NO_OF_BAT_SAMPLES 16 //Multisampling

int player_volume = 79;

static RTC_DATA_ATTR struct timeval sleep_enter_time;
static RTC_DATA_ATTR uint8_t rtcRam[5];
static mode_type_t g_mode = MODE_Filter_A;
static earphone_type g_earphone = WIRED_EARPHONE;
static const char *TAG = "MainCtrl";
static u8g2_t u8g2;
static const adc_channel_t channel = ADC_CHANNEL_0; //GPIO36(SENSOR_VP) if ADC1, GPIO14 if ADC2
static const adc_atten_t atten = ADC_ATTEN_DB_11;
static const adc_unit_t unit = ADC_UNIT_1;
static esp_timer_handle_t bat_timer;
static esp_adc_cal_characteristics_t *bat_adc_chars;
static uint8_t bat_level = 5;
uint8_t glanguage = 0;
static uint8_t busy = false;

esp_err_t esp_bt_send_cmd(int cmd, void *data, int data_len);


int iQuitCounter = 0;
int iNoKeyPressed = 0;
int iCharging = 0;
int iGotoSleep = 60;

int iHSSampleSeconds = 9;	// seconds for sample heart sound
int iLSSampleSeconds = 9;	// seconds for sample lung sound
//int REC_DATA_SIZE = (16*1024*9);

static void check_efuse();
static void print_char_val_type(esp_adc_cal_value_t val_type);
static void bat_measure_cb(void *arg);
static void show_wave();
static void show_headphone(bool show);
//static uint8_t get_language();

static void set_sysConfig(uint8_t language, uint8_t filterType, uint8_t sysVolume, uint8_t sysOutputType);
static void get_sysConfig();

//              31, 62, 125, 250, 500, 1k, 2k, 4kHz, 8kHz and 16kHz
/*
int gainA[] = { -13, 0, 0, 0, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13}; //心音
int gainB[] = { -13, -13, 0, 0, 0, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13}; //肺音
int gainC[] = { -13, 0, 0, 0, 0, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13}; //心肺音
*/


//		  Tested for V1.0.8 
//              31,   62, 125, 250, 500,  1k, 2k,  4kHz, 8kHz 16 | 
int gainA[] = { -13, -13,  12, -13, -13, -13, -13, -13, -13, -13, -13, -13,  12, -13, -13, -13, -13, -13, -13, -13}; //心音
int gainB[] = { -13, -13, -13,   6,  14, -13, -13, -13, -13, -13, -13, -13, -13,   6,  14, -13, -13, -13, -13, -13}; //肺音
int gainC[] = { -13, -13,  10,  10,   0, -13, -13, -13, -13, -13, -13, -13,  10,  10,   0, -13, -13, -13, -13, -13}; //心肺音


/*
//		  Tested for V1.0.9-es8388
//              31,   62, 125, 250, 500,  1k, 2k, 4kHz, 8kHz and 16kHz
int gainA[] = { -13,   6,  18, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13}; //心音
int gainB[] = { -13, -13, -13,  18,  18, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13}; //肺音
int gainC[] = { -13,   6,  18,  18,  18, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13, -13}; //心肺音
*/

SemaphoreHandle_t xSemaphore = NULL;

void app_main(void)
{
	esp_log_level_set("*", ESP_LOG_INFO);
	//esp_log_level_set("AUDIO_PIPELINE", ESP_LOG_DEBUG);
	//esp_log_level_set(TAG, ESP_LOG_INFO);
	
	esp_err_t err = nvs_flash_init();
	if (err == ESP_ERR_NVS_NO_FREE_PAGES)
	{
		ESP_ERROR_CHECK(nvs_flash_erase());
		err = nvs_flash_init();
	}

	struct timeval now;
    gettimeofday(&now, NULL);

    int sleep_time_ms = (now.tv_sec - sleep_enter_time.tv_sec) * 1000 + (now.tv_usec - sleep_enter_time.tv_usec) / 1000;
	switch (esp_sleep_get_wakeup_cause()) {
        case ESP_SLEEP_WAKEUP_EXT1: {
            uint64_t wakeup_pin_mask = esp_sleep_get_ext1_wakeup_status();
            if (wakeup_pin_mask != 0) {
                int pin = __builtin_ffsll(wakeup_pin_mask) - 1;
                printf("Wake up from GPIO %d\n", pin);
            } else {
                printf("Wake up from GPIO\n");
            }
            break;
        }
        case ESP_SLEEP_WAKEUP_TIMER: {
            printf("Wake up from timer. Time spent in deep sleep: %dms\n", sleep_time_ms);
            break;
        }
        case ESP_SLEEP_WAKEUP_UNDEFINED:
        default:
            printf("Not a deep sleep reset\n"); 
			break;
    }

#ifdef CONFIG_ESP_ISTETHO_V0_4_BOARD
	gpio_config_t cfg = {
	    .pin_bit_mask = BIT64(13)|BIT64(14),
	    .mode = GPIO_MODE_DEF_OUTPUT,
	    .pull_up_en = GPIO_PULLUP_DISABLE,
	    .pull_down_en = GPIO_PULLDOWN_DISABLE,
	    .intr_type = GPIO_INTR_DISABLE,
	};
	gpio_config(&cfg);
	gpio_set_level(13, 0); 
	gpio_set_level(14, 0); 
#endif
#ifdef CONFIG_ESP_ISTETHO_V1_0_BOARD
	rtc_gpio_init(GPIO_NUM_12);
	rtc_gpio_set_direction(GPIO_NUM_12, RTC_GPIO_MODE_OUTPUT_ONLY);
	rtc_gpio_set_level(GPIO_NUM_12, 1);
	rtc_gpio_pullup_en( GPIO_NUM_12 );
	rtc_gpio_hold_en(GPIO_NUM_12);
	gpio_config_t cfg = {
	    .pin_bit_mask = BIT64(12)|BIT64(14),
	    .mode = GPIO_MODE_DEF_OUTPUT,
	    .pull_up_en = GPIO_PULLUP_DISABLE,
	    .pull_down_en = GPIO_PULLDOWN_DISABLE,
	    .intr_type = GPIO_INTR_DISABLE,
	};
	gpio_config(&cfg);
	//gpio_set_level(12, 1); 
	//gpio_set_level(14, 1); 
#endif
#ifdef CONFIG_ESP_ISTETHO_V1_2_BOARD
	gpio_config_t cfg = {
	    .pin_bit_mask = BIT64(14)| BIT64(18),
	    .mode = GPIO_MODE_DEF_OUTPUT,
	    .pull_up_en = GPIO_PULLUP_DISABLE,
	    .pull_down_en = GPIO_PULLDOWN_DISABLE,
	    .intr_type = GPIO_INTR_DISABLE,
	};
	gpio_config(&cfg);
	gpio_set_level(14, 0); 
#endif	
	
	gpio_set_level(18, 0); 
	xSemaphore = xSemaphoreCreateMutex();
	vTaskDelay(2 / portTICK_PERIOD_MS);
	gpio_set_level(18, 1); 
	
	check_efuse();
	adc1_config_width(ADC_WIDTH_BIT_12);
	adc1_config_channel_atten(channel, atten);
	//Characterize ADC
	bat_adc_chars = calloc(1, sizeof(esp_adc_cal_characteristics_t));
	esp_adc_cal_value_t val_type = esp_adc_cal_characterize(unit, atten, ADC_WIDTH_BIT_12, DEFAULT_VREF, bat_adc_chars);
	print_char_val_type(val_type);

#ifdef SUPPORT_OLED
	u8g2_esp32_hal_t u8g2_esp32_hal = U8G2_ESP32_HAL_DEFAULT;
	u8g2_esp32_hal.clk = 21;			// CLK
	u8g2_esp32_hal.mosi = 22;			// SDA
	u8g2_esp32_hal.cs = 19;				// CS
	u8g2_esp32_hal.dc = 23;				// CD 
	u8g2_esp32_hal.reset = 18;			// RES
	u8g2_esp32_hal_init(u8g2_esp32_hal);
	u8g2_Setup_ssd1306_128x64_noname_f(
		&u8g2,
		U8G2_R1,
		u8g2_esp32_spi_byte_cb,
		u8g2_esp32_gpio_and_delay_cb); // init u8g2 structure
#endif

	ESP_LOGI(TAG, "Pre time %d\n", (int)sleep_enter_time.tv_sec);
	ESP_LOGI(TAG, "rtcRam[1] [4] %d %d\n", rtcRam[1], rtcRam[4]);

/*
	if (rtcRam[1] != 0xA5)
	{
		rtcRam[1] = 0xA5;					//tag
		rtcRam[0] = MODE_Filter_A;			//sound mode
		rtcRam[2] = 0;						//sleep flag, 1 for sleep
		rtcRam[3] = player_volume;			//play volume
		rtcRam[4] = 0;						//sound to wired or wireless earphone
		//set_sysConfig(glanguage, g_mode, player_volume, g_earphone);
	}
	
	g_earphone = rtcRam[4];
	g_mode = rtcRam[0];
	player_volume = rtcRam[3];
	glanguage = get_language();
*/
	get_sysConfig();
	ESP_LOGI(TAG, "filter-mode %d,  btMode: %d", g_mode,  g_earphone);


#ifdef SUPPORT_OLED
	// Power on OLED
	ESP_LOGE(TAG, "Init display.");
	u8g2_InitDisplay(&u8g2);	 // send init sequence to the display, display is in sleep mode after this,
	u8g2_SetPowerSave(&u8g2, 0); // wake up display
	u8g2_ClearBuffer(&u8g2);
	u8g2_SetFont(&u8g2, u8g2_font_wqy14_t_gb2312);
	u8g2_SetDrawColor(&u8g2, 1);
#endif

	iCharging = 0;
	bat_measure_cb(NULL);

	const esp_timer_create_args_t adc_timer_args = {
		.callback = &bat_measure_cb,
		.name = "bat mesure"};
	ESP_ERROR_CHECK(esp_timer_create(&adc_timer_args, &bat_timer));
	ESP_ERROR_CHECK(esp_timer_start_periodic(bat_timer, BAT_MEASURE_PERIOD));  // 20 seconds loop in bat_measure_cb

	show_sound_level(calc_sound_level());
	show_wave();
	show_bluetooth_connect(false);
	show_mode(g_mode);


	iQuitCounter = 0;

/*
	if(g_earphone == WIRED_EARPHONE){
		iGotoSleep = 60;
		show_trans(false);
		display_update();
		btPhoneModeStart();
	}else {
		iGotoSleep = 360;
		show_headphone(true);
		display_update();
		headPhoneModeStart();
	}
*/

	g_earphone = WIRED_EARPHONE;
	iGotoSleep = 60;
	show_trans(false);
	display_update();
	btPhoneModeStart();

	ESP_LOGI(TAG, "Save mode %d", g_mode);
	ESP_ERROR_CHECK(esp_timer_stop(bat_timer));
	ESP_ERROR_CHECK(esp_timer_delete(bat_timer));

	// Power off OLED
#ifdef CONFIG_ESP_ISTETHO_V0_4_BOARD
	gpio_set_level(13, 0); 
#endif
#ifdef CONFIG_ESP_ISTETHO_V1_0_BOARD
	gpio_set_level(12, 1); 
	gpio_set_level(14, 0); 
#endif
#ifdef CONFIG_ESP_ISTETHO_V1_2_BOARD
	gpio_set_level(14, 1);  
#endif

	gpio_set_level(14, 1); 
	gettimeofday(&sleep_enter_time, NULL);

	set_sysConfig(glanguage, g_mode, player_volume, g_earphone);
	if (rtcRam[2] == 1)
	{
		rtcRam[2] = 0;
		//rtcRam[3] = player_volume;
		ESP_LOGI(TAG, "Prepare to sleep.");

#ifdef  CONFIG_ESP_AI_AUDIO_V1_0_BOARD
		const int ext_wakeup_pin = GPIO_NUM_13;
#endif

#ifdef  CONFIG_ESP_ISTETHO_V0_4_BOARD
		const int ext_wakeup_pin = GPIO_NUM_2;
#endif

#ifdef   CONFIG_ESP_ISTETHO_V1_0_BOARD
		const int ext_wakeup_pin = GPIO_NUM_4;
#endif

#ifdef   CONFIG_ESP_ISTETHO_V1_2_BOARD		
		const int ext_wakeup_pin = GPIO_NUM_4;
#endif

		const uint64_t ext_wakeup_pin_mask = 1ULL << ext_wakeup_pin;
		//vTaskDelay( pdMS_TO_TICKS(2000) );
		while(gpio_get_level(ext_wakeup_pin) == 0){}
		printf("Enabling EXT1 wakeup on pins GPIO%d\n",  ext_wakeup_pin);
		esp_sleep_enable_ext1_wakeup(ext_wakeup_pin_mask, ESP_EXT1_WAKEUP_ALL_LOW);
		
		//vTaskDelay(pdMS_TO_TICKS(500));
		//set_sysConfig(glanguage, g_mode, player_volume, g_earphone);
		ESP_LOGI(TAG, "Enter to sleep.");	
		esp_deep_sleep_start();
}else {
		//const int wakeup_time_msec = 20;
		//ESP_LOGI(TAG, "Enabling timer wakeup, %dms\n", wakeup_time_msec);
		//esp_sleep_enable_timer_wakeup(wakeup_time_msec * 1000);
		rtcRam[2] = 0;
		ESP_LOGI(TAG, "Restart to new Mode.");	
		esp_restart();
	}
}

void change_mode(audio_element_handle_t handle)
{
	int i=0;
	int *gain = gainA;
	if (g_mode == MODE_Filter_A)
	{
		g_mode = MODE_Filter_B;
		gain = gainB;					// Lung sound mode
	}
	else if (g_mode == MODE_Filter_B)
	{
		g_mode = MODE_Filter_C;			
		gain = gainC;					// Normal or Heart-Lung mode
	}
	else if (g_mode == MODE_Filter_C)
	{
		g_mode = MODE_Filter_A;	
		gain = gainA;					// Heart sound mode
	}
	if(handle){
		#ifdef USE_EQUALIZER
		for(i=0;i<20;i++){
			equalizer_set_gain_info(handle,i,gain[i],true);
		}

		#else
			filter_bandpass_setup_set_type(handle,g_mode);
		#endif
	}
	show_mode(g_mode);
	display_update();
	rtcRam[0] = g_mode;
}

mode_type_t get_mode()
{
	return g_mode;
}

void change_earphone_type(){
	if(g_earphone == WIRED_EARPHONE){
		g_earphone = BT_EARPHONE;
	}else {
		g_earphone = WIRED_EARPHONE;
	}
	rtcRam[4] = g_earphone;
	ESP_LOGI(TAG, "filter-mode %d,  btMode: %d", g_mode,  rtcRam[4]);
}

void into_sleep_mode()
{
	rtcRam[2] = 1;
}


void into_ota_mode()
{
	rtcRam[2] = 2;
}

#ifdef CONFIG_ESP_ISTETHO_V1_2_BOARD
static uint8_t soundLevel[5]={20, 40, 60, 80, 100};
#else
static uint8_t soundLevel[5]={31, 40, 48, 56, 63};
#endif

uint8_t calc_sound_level(){
	uint8_t i=0;
	for(i=0;i<5;i++){
		if(player_volume <= soundLevel[i]){
			return i+1;
		}
	}
	return i+1;
}

void show_sound(){
	show_sound_level(calc_sound_level());
	display_update();
}

#define AUDIO_X 6
#define AUDIO_Y 6

#define BAT_X (64 - 5 - Bat_Icon_width)
#define BAT_Y 6

#define WAVE_X 9
#define WAVE_Y 25

#define BMP_X 10
#define BMP_Y 65

#define BMPICON_X 37
#define BMPICON_Y 65

#define MODEICON_X 10
#define MODEICON_Y (BMP_Y+13)

#define BLUET_X (MODEICON_X+6)
#define BLUET_Y (128-16-6)

#define TRANS_X  (64-16)
#define TRANS_Y  BLUET_Y


void display_update()
{
#ifdef SUPPORT_OLED
    if(busy){
        return;
    }

	if( xSemaphoreTake( xSemaphore, ( TickType_t ) 0 ) ) 
	{
		// ESP_LOGI(TAG, "u8g2_SendBuffer.");
		u8g2_SendBuffer(&u8g2);
		xSemaphoreGive(xSemaphore);
	}		
#endif
}

void show_mode(mode_type_t t)
{
#ifdef SUPPORT_OLED
	if (t == MODE_Filter_A)
	{
		if(glanguage == 0)u8g2_DrawXBMP(&u8g2, MODEICON_X, MODEICON_Y, Heart_Icon_width, Heart_Icon_height, Heart_Icon);
		else u8g2_DrawXBMP(&u8g2, MODEICON_X, MODEICON_Y, EHeart_Icon_width, EHeart_Icon_height, EHeart_Icon);
		// u8g2_DrawUTF8(&u8g2, MODE_X, MODE_Y, "心音");
		show_bat_level(bat_level);
		//show_bluetooth_connect(false);
		//REC_DATA_SIZE = 16*1024*iHSSampleSeconds;

	}
	else if (t == MODE_Filter_B)
	{
		if(glanguage == 0)u8g2_DrawXBMP(&u8g2, MODEICON_X, MODEICON_Y, Lung_Icon_width, Lung_Icon_height, Lung_Icon);
		else u8g2_DrawXBMP(&u8g2, MODEICON_X, MODEICON_Y, ELung_Icon_width, ELung_Icon_height, ELung_Icon);
		//u8g2_DrawUTF8(&u8g2, MODE_X, MODE_Y, "肺音");
		show_bat_level(bat_level);
		//show_bluetooth_connect(false);
		//REC_DATA_SIZE = 16*1024*iLSSampleSeconds;
	}
	else if (t == MODE_Filter_C)
	{
		if(glanguage == 0)u8g2_DrawXBMP(&u8g2, MODEICON_X, MODEICON_Y, HLung_Icon_width, HLung_Icon_height, HLung_Icon);
		else u8g2_DrawXBMP(&u8g2, MODEICON_X, MODEICON_Y, EHLung_Icon_width, EHLung_Icon_height, EHLung_Icon);
		//u8g2_DrawUTF8(&u8g2, MODE_X, MODE_Y, "心肺");
		show_bat_level(bat_level);
		//show_bluetooth_connect(false);
		//REC_DATA_SIZE = 16*1024*iHSSampleSeconds;
	}
#endif
}

void show_bat_level(uint8_t level)
{
	if (level > 5)
	{
		level = 5;
	}
#ifdef SUPPORT_OLED
	u8g2_DrawXBMP(&u8g2, BAT_X, BAT_Y, Bat_Icon_width, Bat_Icon_height, &Bat_Icon[level][0]);
#endif
}

void show_sound_level(uint8_t level)
{
	if (level > 5)
	{
		level = 5;
	}
#ifdef SUPPORT_OLED
	u8g2_DrawXBMP(&u8g2, AUDIO_X, AUDIO_Y, Audio_Icon_width, Audio_Icon_height, &Audio_Icon[level][0]);
#endif
}

void show_wave(){
#ifdef SUPPORT_OLED
	u8g2_DrawXBMP(&u8g2, WAVE_X, WAVE_Y, Wave_Icon_width, Wave_Icon_height, Wave_Icon);
#endif	
}

void hide_bluetooth_icon()
{
#ifdef SUPPORT_OLED
	u8g2_DrawXBMP(&u8g2, BLUET_X, BLUET_Y, Clc_Icon_width, Bluetooth_Icon_height, Clc_Icon);
#endif
}

void show_bluetooth_connect(bool connect)
{
#ifdef SUPPORT_OLED
	if (connect)
	{
		u8g2_DrawXBMP(&u8g2, BLUET_X, BLUET_Y, Bluetooth_Icon_width, Bluetooth_Icon_height, &Bluetooth_Icon[0][0]);
	}
	else
	{
		// hide_bluetooth_icon();
		u8g2_DrawXBMP(&u8g2, BLUET_X, BLUET_Y, Bluetooth_Icon_width, Bluetooth_Icon_height, &Bluetooth_Icon[1][0]);
	}
#endif
}

void show_trans(bool show)
{
#ifdef SUPPORT_OLED
	if (show)
	{
		u8g2_DrawXBMP(&u8g2, TRANS_X, TRANS_Y, Trans_Icon_width, Trans_Icon_height, Trans_Icon);
	}
	else
	{
		u8g2_DrawXBMP(&u8g2, TRANS_X, TRANS_Y, Clc_Icon_width, Trans_Icon_height, Clc_Icon);
	}
#endif
}

void show_headphone(bool show)
{
#ifdef SUPPORT_OLED
	if (show)
	{
		u8g2_DrawXBMP(&u8g2, TRANS_X, TRANS_Y, Headp_Icon_width, Headp_Icon_height, Headp_Icon);
	}
	else
	{
		u8g2_DrawXBMP(&u8g2, TRANS_X, TRANS_Y, Clc_Icon_width, Trans_Icon_height, Clc_Icon);
	}
#endif
}

//static float batLevel[6]={3600,3650,3700,3750,3800};
static float batLevel[6]={3310, 3420, 3520, 3660, 3780, 3900};
uint8_t calc_bat_level(float voltage){
	uint8_t i=0;
	for(i=0;i<6;i++){
		if(voltage < batLevel[i]){
			return i;
		}
	}
	return i;
}

//

// loop running every 5 seconds
void bat_measure_cb(void *arg)
{
	//static double bat_voltx[4] = { 0.0 };
	static int bat_volty[4] = { 0 };
	uint32_t adc_reading = 0;
	float vol = 0;
    
	if(busy){
        return;
    }
	//adc_power_acquire();     
   
	//Multisampling
	for (int i = 0; i < NO_OF_BAT_SAMPLES; i++)
	{
		adc_reading += adc1_get_raw((adc1_channel_t)channel);
		vTaskDelay(1);						
	}
	//adc_power_release();

	adc_reading /= NO_OF_BAT_SAMPLES;
	//Convert adc_reading to voltage in mV
	uint32_t voltage = esp_adc_cal_raw_to_voltage(adc_reading, bat_adc_chars);
	vol = voltage * 2.0;

	//ESP_LOGI(TAG, "Bat_Vol: [%5.3f].", vol);

	iNoKeyPressed++;

	//if (iNoKeyPressed == 1)
	{
		for (int i = 0; i<4; i++ )
		{
			bat_volty[i] = vol;
		}
	}

	bat_volty[3] = bat_volty[2]; bat_volty[2] = bat_volty[1]; bat_volty[1] = bat_volty[0];
	//bat_volty[0] = (vol*0.4 + bat_volty[1]*0.3 + bat_volty[2]*0.2 + bat_volty[3]*0.1 );
	bat_volty[0] = (vol + bat_volty[1] + bat_volty[2] + bat_volty[3] ) / 4;
	bat_level = calc_bat_level(vol);
	show_bat_level(bat_level);
	display_update();
	

	if (iNoKeyPressed > iGotoSleep) {
		iQuitCounter++;
	} else {
		iQuitCounter = 0;
	}

	// charging  detecting
	if ( (bat_volty[0] > bat_volty[1]) && (bat_volty[1] > bat_volty[2]) && (bat_volty[2] > bat_volty[3]) && (bat_volty[3]>3000) ) { // 15 second latter
		iCharging++;
		ESP_LOGE(TAG, "Detected Charging [%d] [%d, %d, %d, %d]...", iCharging, bat_volty[0], bat_volty[1], bat_volty[2], bat_volty[3]);	
	} else {
		iCharging = 0;
	}
	
	if (iCharging>1) {
		// directly sleep;

		// clear display
		gpio_set_level(18, 0); 

		// display off
		gpio_set_level(14, 1); 

		iQuitCounter = 100;
		iNoKeyPressed = 100;

		const uint64_t ext_wakeup_pin_mask = 1ULL << GPIO_NUM_4;
		while(gpio_get_level(GPIO_NUM_4) == 0){}
		//printf("Enabling EXT1 wakeup on pins GPIO%d.",  GPIO_NUM_4);
		esp_sleep_enable_ext1_wakeup(ext_wakeup_pin_mask, ESP_EXT1_WAKEUP_ALL_LOW);
		
		set_sysConfig(glanguage, g_mode, player_volume, g_earphone);
		ESP_LOGE(TAG, "Enter to sleep.");	
		esp_deep_sleep_start();
	}

	char  strVBat[18] = {0};
	sprintf(strVBat, "%5.3f%c%c", vol/1000.0, '0'+iHSSampleSeconds, '0'+iLSSampleSeconds);
	memcpy(strVBat+8, &iNoKeyPressed, 4);
	memcpy(strVBat+12, &iQuitCounter, 4);
	
	esp_bt_send_cmd('5', strVBat, 7);
	

	ESP_LOGI(TAG, "Bat_Vol: [%5.3f,%5.3f,%5.3f,%5.3f] \tiNoKeyPressed: %04X,  \tiQuitCounter:  %04X", bat_volty[0]/1000.0, bat_volty[1]/1000.0, bat_volty[2]/1000.0, bat_volty[3]/1000.0, iNoKeyPressed, iQuitCounter);
}

static void check_efuse()
{
	//Check TP is burned into eFuse
	if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP) == ESP_OK)
	{
		printf("eFuse Two Point: Supported\n");
	}
	else
	{
		printf("eFuse Two Point: NOT supported\n");
	}

	//Check Vref is burned into eFuse
	if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_VREF) == ESP_OK)
	{
		printf("eFuse Vref: Supported\n");
	}
	else
	{
		printf("eFuse Vref: NOT supported\n");
	}
}

static void print_char_val_type(esp_adc_cal_value_t val_type)
{
	if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP)
	{
		printf("Characterized using Two Point Value\n");
	}
	else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF)
	{
		printf("Characterized using eFuse Vref\n");
	}
	else
	{
		printf("Characterized using Default Vref\n");
	}
}

/*
static uint8_t get_language(){
    nvs_handle handle_2;
	uint8_t language=0;
    nvs_open("setting", NVS_READWRITE, &handle_2);
	nvs_get_u8(handle_2,"language",&language);
    nvs_close(handle_2);
	return language;
}
*/

void set_language(uint8_t language){
    nvs_handle handle_2;
    nvs_open("setting", NVS_READWRITE, &handle_2);
    nvs_erase_key(handle_2,"language");
	nvs_set_u8(handle_2, "language",language);
    nvs_close(handle_2);
	glanguage = language;
}


static void get_sysConfig(){
    nvs_handle nvsHandle;
	uint8_t language = 0;
	uint8_t filterType = (uint8_t)MODE_Filter_A;	
	uint8_t sysVolume = 49;
	uint8_t sysOutputType = (uint8_t)WIRED_EARPHONE;

	ESP_LOGI(TAG, "default sysConfig: %d, %d, %d, %d\n", language, filterType, sysVolume, sysOutputType);

    nvs_open("setting", NVS_READWRITE, &nvsHandle);

	nvs_get_u8(nvsHandle,"language",&language);
	glanguage = language;

	nvs_get_u8(nvsHandle,"filterType",&filterType);
	//g_mode = filterType;
	g_mode = MODE_Filter_A;

	nvs_get_u8(nvsHandle,"sysVolume",&sysVolume);
	player_volume = sysVolume;

	nvs_get_u8(nvsHandle,"sysOutputType",&sysOutputType);
	g_earphone = sysOutputType;

	ESP_LOGI(TAG, "get sysConfig: %d, %d, %d, %d\n", glanguage, g_mode, player_volume, g_earphone);

    nvs_close(nvsHandle);
}

static void set_sysConfig(uint8_t language, uint8_t filterType, uint8_t sysVolume, uint8_t sysOutputType){
    nvs_handle nvsHandle;

	ESP_LOGI(TAG, "set sysConfig: %d, %d, %d, %d\n", language, filterType, sysVolume, sysOutputType);
    nvs_open("setting", NVS_READWRITE, &nvsHandle);

    nvs_erase_key(nvsHandle,"language");
	nvs_set_u8(nvsHandle, "language",language);
	glanguage = language;

    nvs_erase_key(nvsHandle,"filterType");
	nvs_set_u8(nvsHandle, "filterType",filterType);
	//g_mode = filterType;
	g_mode = MODE_Filter_A;

    nvs_erase_key(nvsHandle,"sysVolume");
	nvs_set_u8(nvsHandle, "sysVolume",sysVolume);
	player_volume = sysVolume;

    nvs_erase_key(nvsHandle,"sysOutputType");
	nvs_set_u8(nvsHandle, "sysOutputType",sysOutputType);
	g_earphone = sysOutputType;

    nvs_close(nvsHandle);
}

void set_wifiConfig(char* strSSID, char* strPwd, char *strOtaUrl){
   nvs_handle nvsHandle;

	ESP_LOGI(TAG, "set SSID: %s, Pwd: %s, otaUrl: %s\n",  strSSID, strPwd, strOtaUrl);
    nvs_open("setting", NVS_READWRITE, &nvsHandle);

	if (strlen(strSSID) > 6) {
		nvs_erase_key(nvsHandle,"wifiSSID");
		nvs_set_str(nvsHandle, "wifiSSID",strSSID);
	}

	if (strlen(strPwd) > 6) {
    	nvs_erase_key(nvsHandle,"wifiPwd");
		nvs_set_str(nvsHandle, "wifiPwd",strPwd);
	}

	if (strlen(strOtaUrl) > 6) {
		nvs_erase_key(nvsHandle,"otaUrl");
		nvs_set_str(nvsHandle, "otaUrl",strOtaUrl);
	}

	nvs_close(nvsHandle);
}

#include "esp_partition.h"
void erase_ota_data_partition(void)
{
    const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_OTA, "otadata");
	esp_partition_erase_range( partition,  0,  partition->size );
	into_ota_mode();
	
	// Power off OLED
#ifdef CONFIG_ESP_ISTETHO_V0_4_BOARD
	gpio_set_level(13, 0); 
#endif
#ifdef CONFIG_ESP_ISTETHO_V1_0_BOARD
	gpio_set_level(12, 1); 
#endif

	gpio_set_level(14, 0);  
}

void set_busy(uint8_t status){
    busy = status;
}


